iT邦幫忙

2022 iThome 鐵人賽

DAY 19
0
Software Development

或躍在淵的CAE: 讓咱們用Python會一會ANSA + LS-DYNA系列 第 19

[Day19] - Box Drop Project精進計畫(11) - 使用Streamlit於本機端產生config檔

  • 分享至 

  • xImage
  •  

截至[Day18]為止,我們的platebox參數都是寫死在main裡,今天我們來試試如何將這些參數抽取出來。

Config dict

一個直覺的方式是直接將所有想要調整的變數直接抽出來,作為main的參數。這個方法雖然可行,但是每次參數有所增刪時,都要記得修改。我們的作法是讓main吃一個config dict,再於main內取出config中的參數值。

下面除了platebox的參數外,還另外抽取了初始速度及結束時間兩個參數作為示範。

#box_drop.py
def main(config):
    l_p = config['l_p']
    w_p = config['w_p']
    en1_p = config['en1_p']
    en2_p = config['en2_p']
    z_elv_p = config['z_elv_p']
    move_x_p = config['move_x_p']
    move_y_p = config['move_y_p']
    move_xy_p = (move_x_p, move_y_p)
    rot_angle_p = config['rot_angle_p']

    l_b = config['l_b']
    w_b = config['w_b']
    h_b = config['h_b']
    en1_b = config['en1_b']
    en2_b = config['en2_b']
    en3_b = config['en3_b']
    z_elv_b = config['z_elv_b']
    move_x_b = config['move_x_b']
    move_y_b = config['move_y_b']
    move_xy_b = (move_x_b, move_y_b)
    rot_angle_b = config['rot_angle_b']

    vz = config['vz']
    endtim = config['endtim']
    
    initial_velocity = create_initial_velocity(
        {'NSID': box_set._id, 'VZ': vz}, deck=deck)
    ctrl_params = [('TERMINATION', {'ENDTIM': endtim})]

下一步是如何產生這個config dict,除了直接用打的之外,如果能有個GUI介面也是不錯的選擇。

Streamlit

以下我們使用Streamlit來建立一個GUI介面。

A faster way to build and share data apps

Streamlit turns data scripts into shareable web apps in minutes.
All in pure Python. No front‑end experience required.

Streamlit將很多需要JavaScript及CSS才能有的前端體驗打包起來,讓我們可以用純Python建立一個可以分享的app,而不需要煩惱太多前端的設定。

  • 建立一個.streamlit資料夾,並於其內產生config.tomlsecrets.toml兩個檔案。
  • config.toml可以儲存對Streamlit的設定,這邊我們指定使用dark主題。
  • secrets.toml可以用來儲存一些敏感資訊,有點像是Python的.env檔案。
  • 將所需的三個packageStreamlitboto3pydantic 寫入requirements.txt
  • 產生一個Python虛擬環境venv,並於啟動後安裝requirements.txt內的package
mkdir .streamlit 
touch .streamlit/{config,secrets}.toml 
echo -e "[theme]\nbase = \"dark\"" >> .streamlit/config.toml 
echo -e "streamlit\nboto3\npydantic" >> requirements.txt 
python3 -m venv venv 
source venv/bin/activate 
pip install -r requirements.txt 

再來,我們建立一個st_app.py來撰寫Streamlit的相關程式。
Streamlit內有很多widget可以使用,我們簡單介紹幾個有使用到的。

  • Container可以幫助我們有序分類各種元素。
  • Number Input可以接收數字,恰恰是我們這次config dict所需要的。Number Input可以設定名字、預設值及最大最小值等。
  • Form可以將其內所含的Widget值一次submit,需要搭配form_submit_button一起使用。
  • Columns可以讓我們調整橫向的每欄寬度。

pydantic

最後我們使用了pydantic來作為寫出config檔之前的parse工作,讓我們寫出一致的格式。其用法相當直觀,建立一個繼承BaseModelclass,在其中定義變數名,並指定想要的type,即會盡可能達成parse工作。

整體流程

  • 先為plateboxcontrol params建立三個pydantic model
#st_app.py
import json

import streamlit as st
from pydantic import BaseModel

st.set_page_config('Box Drop',  layout='centered')
st.header('Box Drop Config')


class PlateModel(BaseModel):
    l_p: float
    w_p: float
    en1_p: int
    en2_p: int
    z_elv_p: float
    move_x_p: float
    move_y_p: float
    rot_angle_p: float


class BoxModel(BaseModel):
    l_b: float
    w_b: float
    h_b: float
    en1_b: int
    en2_b: int
    en3_b: int
    z_elv_b: float
    move_x_b: float
    move_y_b: float
    rot_angle_b: float


class CRTLParamsModel(BaseModel):
    vz: float
    endtim: float
  • 建立一個st.form,於其內建立三個st.container, 用來分類plateboxcontrol params。每個container中的widget會使用st.columns加以排版。
#st_app.py
def main():
    with st.form('submit-form'):
        plate = st.container()
        box = st.container()
        crtl_params = st.container()

        with plate:
            st.write('Plate')

            col_11, col_12 = st.columns(2)
            with col_11:
                l_p = st.number_input('l_p(mm)', value=100.0, step=1.0)
            with col_12:
                w_p = st.number_input('w_p(mm)', value=100.0, step=1.0)

            col_21, col_22 = st.columns(2)
            with col_21:
                en1_p = st.number_input('en1_p', value=10, min_value=1)
            with col_22:
                en2_p = st.number_input('en2_p', value=10, min_value=1)

            col_31, col_32, col_33, col_34 = st.columns(4)
            with col_31:
                z_elv_p = st.number_input('z_elv_p', value=0.0, step=1.0)
            with col_32:
                move_x_p = st.number_input('move_x_p(mm)', value=0.0, step=1.0)
            with col_33:
                move_y_p = st.number_input('move_y_p(mm)', value=0.0, step=1.0)
            with col_34:
                rot_angle_p = st.number_input(
                    'rot_angle_p(deg)', value=0.0, min_value=0.0, max_value=360.0, step=1.0)

        with box:
            st.write('Box')

            col_51, col_52, col_53 = st.columns(3)
            with col_51:
                l_b = st.number_input('l_b(mm)', value=50.0, step=1.0)
            with col_52:
                w_b = st.number_input('w_b(mm)', value=50.0, step=1.0)
            with col_53:
                h_b = st.number_input('h_b(mm)', value=50.0, step=1.0)

            col_61, col_62, col_63 = st.columns(3)
            with col_61:
                en1_b = st.number_input('en1_b', value=10, min_value=1)
            with col_62:
                en2_b = st.number_input('en2_b', value=10, min_value=1)
            with col_63:
                en3_b = st.number_input('en3_b', value=10, min_value=1)

            col_71, col_72, col_73, col_74 = st.columns(4)
            with col_71:
                z_elv_b = st.number_input('z_elv_b(mm)', value=5.0, step=1.0)
            with col_72:
                move_x_b = st.number_input(
                    'move_x_b(mm)', value=50.0, step=1.0)
            with col_73:
                move_y_b = st.number_input(
                    'move_y_b(mm)', value=20.0, step=1.0)
            with col_74:
                rot_angle_b = st.number_input(
                    'rot_angle_b(deg)', value=45.0, min_value=0.0, max_value=360.0, step=1.0)

        with crtl_params:
            st.write('Control params')

            col_101, col_102 = st.columns(2)
            with col_101:
                vz = st.number_input('vz(mm/s)', value=-500.0, step=1.0)
            with col_102:
                endtim = st.number_input(
                    'end_time(s)', value=1.5E-2,  min_value=0.0)

        submit_button = st.form_submit_button('Submit')
  • 最後如果按下submit button後,我們將plateboxcontrol params分別丟入相對應的pydantic model進行parse後,並利用pydanticdict function將其裝入一個名為datadict後,將其寫出一個名為input_data.jsonjson檔案於本機端。
#st_app.py
       if submit_button:
            plate_model = PlateModel(**{'l_p': l_p,
                                        'w_p': w_p,
                                        'en1_p': en1_p,
                                        'en2_p': en2_p,
                                        'z_elv_p': z_elv_p,
                                        'move_x_p': move_x_p,
                                        'move_y_p': move_y_p,
                                        'rot_angle_p': rot_angle_p})

            box_model = BoxModel(**{'l_b': l_b,
                                    'w_b': w_b,
                                    'h_b': h_b,
                                    'en1_b': en1_b,
                                    'en2_b': en2_b,
                                    'en3_b': en3_b,
                                    'z_elv_b': z_elv_b,
                                    'move_x_b': move_x_b,
                                    'move_y_b': move_y_b,
                                    'rot_angle_b': rot_angle_b})

            crtl_params_model = CRTLParamsModel(**{'vz': vz, 'endtim': endtim})

            data = {**plate_model.dict(),
                    **box_model.dict(),
                    **crtl_params_model.dict()}

            filename = 'input_data.json'
            with open(filename, 'w') as f:
                json.dump(data, f)

            now = datetime.now().strftime('%Y%m%d_%H%M%s')
            st.info(f'{filename} is written at {now}.')
  • 於主程式中讀入這個config檔並傳給main,即可透過ANSA建立模型並求解完畢。
#box_drop.py
if __name__ == '__main__':
    jfile = Path.cwd() / 'input_data.json'
    with open(jfile) as f:
        config = json.load(f)
    main(config)
  • terminal中輸入streamlit run st_app.py開啟這個App
    開啟後畫面如下:

st-box-drop-config

Code

本日程式碼傳送門


上一篇
[Day18] - Box Drop Project精進計畫(10) - Test
下一篇
[Day20] - Box Drop Project精進計畫(12) - 部署於Streamlit Cloud,可於遠端產生config檔並上傳至Linode Object Storage
系列文
或躍在淵的CAE: 讓咱們用Python會一會ANSA + LS-DYNA30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言